#ifndef DAQ_H
#define DAQ_H

#include <inttypes.h>
#include "../util/util.h"
#include "../packets/crc.h"
#include "../ftd2xx.h"

#include "../packets/structs/adcPacketStructs.h"
#include "../packets/structs/dacPacketStructs.h"
#include "../packets/structs/dioPacketStructs.h"
#include "../packets/structs/encPacketStructs.h"
#include "../packets/structs/genPacketStructs.h"
#include "../packets/structs/msgHeaderDefs.h"
#include "../packets/structs/pwmPacketStructs.h"
#include "../packets/structs/tmrPacketStructs.h"

#define bufferSize  65535
#define numberOfSamples 1024

/* ---<>-----------------------------------------------------------------------------------
		Where is this defined?
-------------------------------------------------------------------------------------------- */
uint16_t packetSizeLookup(uint8_t msgHeader);

/* ---<>-----------------------------------------------------------------------------------
		ringBuffer class
-------------------------------------------------------------------------------------------- */
class ringBuffer
{
 public:
    uint8_t *bufferBegin;
    uint8_t *bufferEnd;
    uint8_t *head;
    uint8_t *tail;
    uint8_t *vTail;
    uint32_t bytesInBuffer;	
    bool    dataOverFlow;
    uint8_t data[bufferSize];
    ringBuffer();
	void bufferWrite(char c);
	uint8_t bufferRead();
    status_t receiveFunc();
    uint8_t vBufferRead();
    void flush();
};


/* ---<>-----------------------------------------------------------------------------------
		DAQALLOC
-------------------------------------------------------------------------------------------- */
namespace DAQ_namespace
{
    class DAQALLOC
    {
		public:
        //settingPending flags will halt additional settings until the first is heard
        //example:
        //if adc channel 0 is set to 2x gain wait for the confirmation till
        //allowing a msg to set the gain to 4x
        //settingPending(s) + watchdog(s) can be used to resend commands

        struct analogInAlloc{
            bool        settingPending;
            bool        enable;
            bool        sign;
            GAIN_ENUM   gain;
            SMPD_t      samples;
            RES_t       resolution;
        }analogIn[4];

        struct analogOutAlloc{
            bool    settingPending;
            bool    enable;
        }analogOut[2];

        //settings other than enable should be set by this program
        //ack error codes should tell whether a message was received correctly
        //there should be prevention from assigning the same encoder different values per run
        struct encoderAlloc{
            bool        settingPending;
            bool        enable;
            bool        absolute;
            bool        index;
            PLOC_ENUM   PLOC;
            uint16_t    resolution;    //prior to upscaling
        }encoder[2];

        struct digitalAlloc{
            bool    inSettingPending[13];
            bool    outSettingPending[13];
            bool    inEnable;
            bool    outEnable;
            bool    dioAlloc[13];
            ioSrc   src[13];        //direction for the pin
        }digital;

        struct motorAlloc{
            bool        settingPending;
            bool        enable;
        }motor[4];

        struct servoAlloc{
            bool        settingPending;
            bool        enable;
        }servo[4];

        struct genAlloc
        {
            bool        startPending;
            bool        stopPending;
            bool        actuatorSuperPending;
            bool        outputSuperPending;
        }general;

        struct timerAlloc
        {
            bool        settingPending;
            bool        enable;
            uint16_t    loopTime;
        }timer;

        DAQ_STATE_t   state;

        DAQALLOC();

        status_t inputEnabled();
        status_t outputEnabled();


        void analogInConfig(uint8_t errorCode);
        void analogOutConfig(uint8_t errorCode);
        void encoderConfig(uint8_t errorCode);
        void encDioConfig(uint8_t encNum);
        void digitalInConfig(uint8_t errorCode);
        void digitalOutConfig(uint8_t errorCode);
        void motorConfig(uint8_t errorCode);
        void servoConfig(uint8_t errorCode);

        void startConfig(uint8_t errorCode);
        void stopConfig(uint8_t errorCode);
        void readyConfig();
        void tmrConfig(uint8_t errorCode);

        //these functions will assess what features are enabled
        uint8_t getOutputSuperPacketLength();
        uint8_t getInputSuperPacketLength();
        uint8_t getActuatorSuperPacketLength();

        //advanced
        void sendAdcInit( ACHM_t adcMsk,bool sign,GAIN_t adcGain,SMPD_t sampleDepth,RES_t resolution);

        //simple
        status_t sendAdcInit( uint8_t adcMsk, GAIN_t adcGain);

        status_t sendReset();
        status_t sendDacEnable(uint8_t DACM);

        status_t sendDigitalInEnable(uint16_t data);
        status_t sendDigitalOutEnable(uint16_t data);

        status_t sendEncoderInit( PLOC_ENUM PLOC, bool index, uint8_t num, bool ABS, DIGFILT_ENUM EDFL, uint16_t resolution);

        status_t sendMotorEnable( uint8_t msk);
        status_t sendServoEnable( uint8_t msk);

        status_t sendSetTimerFreq(uint16_t freq);

        status_t sendStartProgram();
        status_t sendStopProgram();

        status_t sendOutputSuperPacket( uint16_t analogOut[], uint16_t dioSet, uint16_t dioClr);
        status_t sendActuatorSuperPacket( uint16_t servoPosition[], uint16_t motorEffort[]);

        void inputSuperPacketHandler(uint8_t packet[]);
    };
}
#endif // DAQ_H
